/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.core.impl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import com.cqcis.uip.base.framework.common.Role;
import com.cqcis.uip.base.framework.core.ContextStartNotify;
import com.cqcis.uip.base.framework.core.RegisteredService;
import com.cqcis.uip.base.framework.core.ServiceRole;
import com.cqcis.uip.base.framework.core.ServiceRoleIgnore;
import com.cqcis.uip.base.framework.utils.AnnotationUtil;
import com.cqcis.uip.base.framework.utils.LogServiceThreadPoolExecutor;
import com.cqcis.uip.base.framework.utils.LogServiceThreadPoolExecutor.LogThreadPoolExecutor;

/**
 * 将自定义的service层与RegisteredService相关联
 * 
 * @author huangmy
 * @date   2008-6-20
 */
public class AutoInjectServiceBeanPostProcessor implements BeanPostProcessor, ContextStartNotify {
	private final static Log log = LogFactory.getLog(AutoInjectServiceBeanPostProcessor.class);
	public final static Collection<LogThreadPoolExecutor> PUBLISH_RS_TPE = Collections.synchronizedSet(new HashSet<LogThreadPoolExecutor>());
	private List<ObjectType> services = new ArrayList<ObjectType>(); // 带注解@ServiceRole的服务
	private List<ObjectType> hostObjects = new ArrayList<ObjectType>(); // 宿主实例	
	private CountDownLatch aiWaiter = new CountDownLatch(1); // 协调等待计数器
			
	private static class ObjectType {
		private Object object;
		private Role role;
		private String property = "";
		
		public ObjectType(Object object, Role role) {
			this.object = object;
			this.role = role;
		}
		
		public ObjectType(Object object, Role role, String property) {
			this.object = object;
			this.role = role;
			this.property = property;
		}

		@Override
		public String toString() {
			return "{ObjectType: Class = " + object.getClass() 
			     + ", Role = " + role + ", Property = " + property +"}";
		}
		
		public Object getObject() {
			return object;
		}

		public void setObject(Object object) {
			this.object = object;
		}

		public Role getRole() {
			return role;
		}

		public void setRole(Role role) {
			this.role = role;
		}

		public String getProperty() {
			return property;
		}

		public void setProperty(String property) {
			this.property = property;
		}
	}
	
	/**
	 * 获取类上的服务角色注解，如果没有，继续查找实现的接口
	 * 
	 * @param clazz
	 * @return
	 */
	private ServiceRole getServiceRoleAnnotationPresent(Class<?> clazz) {
		return (ServiceRole)AnnotationUtil.findDeepAnnotationForClass(clazz, ServiceRole.class, ServiceRoleIgnore.class);		
	}
	
	public synchronized Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {			
		ServiceRole role = getServiceRoleAnnotationPresent(bean.getClass());
		if (role != null) {	
			this.services.add(new ObjectType(bean, role.value(), role.property()));
		}
		
		if (this.services.size() > 1 && !Role.RolesMultiTypes.contains(role.value())) {
			List<String> cs = new ArrayList<String>();
			for (ObjectType s : this.services) {
				cs.add(s.getObject().getClass().getName());
			}
			throw new RuntimeException("the beans of 'ServiceRole' can't be more than 1. the beans type is: " +
					cs);
		}
		
		return bean;
	}
	
	/**
	 * 是否标注方法
	 * 
	 * @param m
	 * @return
	 */
	private void checkVaildAnnotationRoleMethod(Method m) {
		Class<?>[] parameterTypes = m.getParameterTypes();
		if (parameterTypes == null || parameterTypes.length != 1) {
			throw new BeanInitializationException("method '" + m + "' is not a valid for injection");
		}
	}
	
	/**
	 * 调用方法
	 * 
	 * @param m
	 * @param bean
	 * @param s
	 */
	private void invokeRoleMethod(Method m, Object bean, Object s) {
		if (bean == s) { // 同一个实例，不注入
			return;
		}
		try {
			m.invoke(bean, s);
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException(e);
		}
		
		if (log.isDebugEnabled()) {
			log.debug("Annotation Injected: method=" + m.getName() 
					+ ", bean=" + bean.getClass() 
					+ ", param=" + s.getClass());
		}
	}
	
	public synchronized void notifyContextStart() {
		this.aiWaiter.countDown(); // notify publish RS	    
	}
	
	public synchronized Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {		
		Method[] methods = AnnotationUtil.findAnnotationMethodsFor(bean.getClass(), ServiceRole.class);			
		if (methods != null) {
			for (Method m : methods) {
			    checkVaildAnnotationRoleMethod(m); // check valid
			    ServiceRole role = m.getAnnotation(ServiceRole.class);
				for (Iterator<ObjectType> it = services.iterator(); it.hasNext();) {
					ObjectType s = it.next();
					if (role.value() == s.getRole() 
					         && role.property().equals(s.getProperty())) {
						invokeRoleMethod(m, bean, s.getObject());
						it.remove(); // remove
					}
				}
				this.hostObjects.add(new ObjectType(bean, role.value(), role.property()));
			}
		}
		
		for (Iterator<ObjectType> it = services.iterator(); it.hasNext();) {
			ObjectType s = it.next();
			boolean injected = false;
			for (ObjectType h : hostObjects) {				
				Method[] ms = AnnotationUtil.findAnnotationMethodsFor(h.getObject().getClass(), ServiceRole.class);				
				if (ms != null) {
					for (Method m : ms) {
					    checkVaildAnnotationRoleMethod(m); // check valid
						ServiceRole role = m.getAnnotation(ServiceRole.class);						
						if (role.value() == s.getRole() 
						         && role.property().equals(s.getProperty())) {
							invokeRoleMethod(m, h.getObject(), s.getObject());
							injected = true;
						}
					}
				}
			}
			
			if (injected) {
				it.remove();
			}
		}
		
		if (bean instanceof RegisteredService) {
			// 发布RS线程
			final LogThreadPoolExecutor publishRSWaitTPE = (LogThreadPoolExecutor)LogServiceThreadPoolExecutor.getLogExecutor(1, 
					2, 120, null, null, "PublishRS-" + ((RegisteredService) bean).getName());
			PUBLISH_RS_TPE.add(publishRSWaitTPE);
			
			publishRSWaitTPE.execute(new Runnable() {				
				public void run() {
					try {
						aiWaiter.await();
					} catch (InterruptedException e) {						
					}
					// publish RegisteredService
					((RegisteredService) bean).getBundleContext().registerService(RegisteredService.class.getName(),
							bean, null);
										
					PUBLISH_RS_TPE.remove(publishRSWaitTPE);
					publishRSWaitTPE.shutdown();					
				}
			});
		}
		
		return bean;
	}

}
